package nbt

import (
	"bytes"
	"encoding/binary"
	"io"
	"strings"
	"sync"
)

var (
	NBT_HEADER    = [...]byte{0x0a, 0x00, 0x00, 0x09, 0x00, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x0a}
	STRING_HEADER = [...]byte{0x08}
	END           = [...]byte{0x00}
)

type McServer struct {
	Ip   string
	Name string
}

type McServers struct {
	Servers []McServer
	mu      sync.Mutex
}

func writeName(w io.Writer, name string) (int64, error) {
	totalWritten := int64(0)

	nameLen := uint16(len(name))
	r := bytes.NewReader([]byte{byte(nameLen << 8), byte(nameLen)})

	n, err := io.Copy(w, r)
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	n, err = io.Copy(w, strings.NewReader(name))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	return totalWritten, nil
}

func writeString(w io.Writer, name string, data string) (int64, error) {
	totalWritten := int64(0)

	n, err := io.Copy(w, bytes.NewReader(STRING_HEADER[:]))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	n, err = writeName(w, name)
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	dataLen := uint16(len(data))
	r := bytes.NewReader([]byte{byte(dataLen << 8), byte(dataLen)})

	n, err = io.Copy(w, r)
	totalWritten += n

	if err != nil {
		return totalWritten, err
	}

	n, err = io.Copy(w, strings.NewReader(data))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	return totalWritten, nil
}

func writeServer(w io.Writer, s McServer) (int64, error) {
	totalWritten := int64(0)

	n, err := writeString(w, "ip", s.Ip)
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	n, err = writeString(w, "name", s.Name)
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	n, err = io.Copy(w, bytes.NewReader(END[:]))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	return totalWritten, nil
}

func NewMcServers() *McServers {
	result := &McServers{}

	result.Servers = make([]McServer, 0)

	return result
}

func (nbt *McServers) AddServer(s McServer) {
	nbt.mu.Lock()
	nbt.Servers = append(nbt.Servers, s)
	nbt.mu.Unlock()
}

func (nbt *McServers) GetLen() int {
	nbt.mu.Lock()
	length := len(nbt.Servers)
	nbt.mu.Unlock()

	return length
}

func (nbt *McServers) WriteTo(w io.Writer) (int64, error) {
	totalWritten := int64(0)

	n, err := io.Copy(w, bytes.NewReader(NBT_HEADER[:]))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	} // root tag header + `servers` header

	err = binary.Write(w, binary.BigEndian, uint32(len(nbt.Servers)))
	if err != nil {
		return totalWritten, err
	} // `servers` length
	totalWritten += 4

	for i := 0; i < len(nbt.Servers); i++ {
		n, err = writeServer(w, nbt.Servers[i])
		totalWritten += n
		if err != nil {
			return totalWritten, err
		}
	}

	n, err = io.Copy(w, bytes.NewReader(END[:]))
	totalWritten += n
	if err != nil {
		return totalWritten, err
	}

	return totalWritten, nil
}
